#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <sys/un.h>
#include <unistd.h>
#include <stdbool.h>

#include <arpa/inet.h>

#if (BOARD == 1)
#include "event2/listener.h"
#include "event2/bufferevent.h"
#include "event2/buffer.h"
#include "event2/thread.h"
#include "event2/bufferevent_ssl.h"
#include "event2/http.h"
#include "event2/util.h"
#include "event2/keyvalq_struct.h"
#else

#include <event2/listener.h>
#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <event2/thread.h>
#include <event2/bufferevent_ssl.h>
#include <event2/http.h>
#include <event2/util.h>
#include <event2/keyvalq_struct.h>


#endif

#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/rand.h>

#include <stdbool.h>
#include <pthread.h>

#include "debug.h"
#include "libevent_socket_wrapper.h"

#define PORT_STR_BUFSZ   6

static int fail_with_errno(evsocket_result_ts *out, evsocket_status_te status);

static int check_config(evsocket_config_ts *cfg, evsocket_result_ts *res);

static void
accept_conn_cb(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *address, int socklen, void *ctx);

static struct bufferevent *httpbevcb(struct event_base *base, void *arg);

static int release_acception(void *ctx, struct list_head *hd);

#if 0
static int release_all_acception(void * ctx, struct list_head * hd);
#endif

static void event_callback(struct bufferevent *bev, short events, void *ctx);

static void accept_error_cb(struct evconnlistener *listener, void *ctx);

static void read_callback(struct bufferevent *bev, void *ctx);

static void write_callback(struct bufferevent *bev, void *ctx);

static void socket_set_hints(evsocket_config_ts *cfg, struct addrinfo *hints);

static int set_socket_options(evsocket_config_ts *cfg, evsocket_result_ts *res, int fd);

static int make_unixdomain(evsocket_config_ts *cfg, evsocket_result_ts *res);

static int make_inet(evsocket_config_ts *cfg, evsocket_result_ts *out);

static int make_http(evsocket_config_ts *cfg, evsocket_result_ts *out);

static void ssl_init(void);

//static int cert_verify_callback(X509_STORE_CTX *x509_ctx, void *arg);

static int ssl_setup_certs(SSL_CTX *ctx, const char *server_crt, const char *private_key);


static int fail_with_errno(evsocket_result_ts *out, evsocket_status_te status)
{
    out->status = status;
    out->saved_errno = errno;
    errno = 0;
    return status;
}

static int check_config(evsocket_config_ts *cfg, evsocket_result_ts *res)
{
    int fd;
    int r;

    if (cfg == NULL)
    {
        return fail_with_errno(res, EVSOCKET_ERROR_INVALID);
    }

    //检查有效性
    if (cfg->type <= EVSOCKET_TYPE_MIN || cfg->type >= EVSOCKET_TYPE_MAX)
    {
        return fail_with_errno(res, EVSOCKET_ERROR_TYPE);
    }

    if (cfg->role <= ROLE_MIN || cfg->role >= ROLE_MAX)
    {
        return fail_with_errno(res, EVSOCKET_ERROR_ROLE);
    }

    //若设置listen backlog参数为0，将导致evconnlistener_new_bind失败，服务器也无法接受连接
    if (cfg->backlog == 0)
    {
        cfg->backlog = -1;
    }

    if (cfg->af == EV_FAMILY_UNIXDOMAIN && cfg->role == ROLE_SERVER)
    {
        if (cfg->path != NULL)
        {
            //通过测试能否创建文件，检查路径的合法性和权限
            fd = creat(cfg->path, 0666);

            if (fd == -1)
            {
                if (errno != EEXIST && errno != ENXIO)
                {
                    return fail_with_errno(res, EVSOCKET_ERROR_CREAT);
                }
            }

            unlink(cfg->path);
        }
        else
        {
            return fail_with_errno(res, EVSOCKET_ERROR_PATH);
        }
    }

    if (cfg->base == NULL)
    {
        //是否需要支持多线程
        if (cfg->bevflags & BEV_OPT_THREADSAFE || cfg->levflags & LEV_OPT_THREADSAFE)
        {
            evthread_use_pthreads();
        }

        cfg->base = event_base_new();

        if (cfg->base == NULL)
        {
            return fail_with_errno(res, EVSOCKET_ERROR_NEW_BASE);
        }
    }

    if (cfg->type == EVSOCKET_TYPE_TLS || cfg->type == EVSOCKET_TYPE_HTTPS)
    {
        ssl_init();

        /* This isn't strictly necessary... OpenSSL performs RAND_poll
         * automatically on first use of random number generator. */
        r = RAND_poll();
        if (r == 0)
        {
            DEBUG_PRINTF(ERROR, "RAND_poll");
            return fail_with_errno(res, EVSOCKET_ERROR_SSL_CTX_NEW);
        }

        /* Create a new OpenSSL context */
        cfg->ssl_ctx = SSL_CTX_new(SSLv23_method());
        if (!cfg->ssl_ctx)
        {
            return fail_with_errno(res, EVSOCKET_ERROR_SSL_CTX_NEW);
        }

        //加载CA根证书
        if (1 != SSL_CTX_load_verify_locations(cfg->ssl_ctx, cfg->ca_crt, NULL))
        {
            DEBUG_PRINTF(ERROR, "SSL_CTX_load_verify_locations");
            return fail_with_errno(res, EVSOCKET_ERROR_SSL_SETCERT);
        }

        if (cfg->role == ROLE_CLIENT)
        {
            //作为client,单向认证,检查对端(服务器)证书
            SSL_CTX_set_verify(cfg->ssl_ctx, SSL_VERIFY_PEER, NULL);
//
//            SSL_CTX_set_cert_verify_callback(cfg->ssl_ctx, cert_verify_callback,
//                                             (void *) cfg->host);
            cfg->ssl = SSL_new(cfg->ssl_ctx);
            if (cfg->ssl == NULL)
            {
                return fail_with_errno(res, EVSOCKET_ERROR_SSL_NEW);
            }
        }
        else
        {
            if (EVSOCKET_OK != ssl_setup_certs(cfg->ssl_ctx, cfg->server_crt, cfg->private_key))
            {
                return fail_with_errno(res, EVSOCKET_ERROR_SSL_SETCERT);
            }
        }
    }

    //初始化client连接信息链表
    if (cfg->role == ROLE_SERVER && (cfg->type == EVSOCKET_TYPE_TLS || cfg->type == EVSOCKET_TYPE_TCP))
    {
        pthread_mutex_init(&cfg->acceptlist_mtx, NULL);
        pthread_mutex_lock(&cfg->acceptlist_mtx);
        INIT_LIST_HEAD(&cfg->acceptlist);
        pthread_mutex_unlock(&cfg->acceptlist_mtx);
    }

    return EVSOCKET_OK;
}

/*****************************************************************************
 函 数 名  : accept_conn_cb
 功能描述  : server端accept回调
 输入参数  : struct evconnlistener* listener
             evutil_socket_t fd
             struct sockaddr* address
             int socklen
             void* ctx
 输出参数  : 无
 返 回 值  : static void
 调用函数  :
 被调函数  :

 修改历史      :
  1.日    期   : 2017年8月29日
    作    者   : thomas
    修改内容   : 新生成函数

*****************************************************************************/
static void accept_conn_cb(struct evconnlistener *listener, evutil_socket_t fd,
                           struct sockaddr *address, int socklen, void *ctx)
{
    SSL *ssl = NULL;
    struct event_base *base;
    struct bufferevent *bev;
    evsocket_config_ts *conf;
    accept_bufferevent_list_ts *acceptbufferevent;

    if (listener == NULL || ctx == NULL)
    {
        return;
    }

    conf = (evsocket_config_ts *) ctx;
    base = evconnlistener_get_base(listener);

    if (conf->type == EVSOCKET_TYPE_TLS)
    {
        ssl = SSL_new(conf->ssl_ctx);

        if (NULL == ssl)
        {
            DEBUG_PRINTF(ERROR, "SSL_new failed");
            return;
        }

        bev = bufferevent_openssl_socket_new(base, fd, ssl, BUFFEREVENT_SSL_ACCEPTING,
                                             conf->bevflags);
    }
    else
    {
        bev = bufferevent_socket_new(base, fd, conf->bevflags);
    }

    //将连接的客户端bufferevent，fd等信息插入到链表尾
    acceptbufferevent = (accept_bufferevent_list_ts *) malloc(sizeof(accept_bufferevent_list_ts));
    if (acceptbufferevent == NULL)
    {
        DEBUG_PRINTF(ERROR, "malloc falied");
        return;
    }
    acceptbufferevent->bev = bev;
    acceptbufferevent->fd = fd;

    pthread_mutex_lock(&conf->acceptlist_mtx);
    list_add(&(acceptbufferevent->list), &(conf->acceptlist));
    pthread_mutex_unlock(&conf->acceptlist_mtx);

    DEBUG_PRINTF(INFO, "accept connection,bev[%p]", bev);
    bufferevent_setcb(bev, read_callback, write_callback, event_callback, conf);
    bufferevent_enable(bev, EV_READ);
    bufferevent_disable(bev, EV_WRITE);
}

/*****************************************************************************
 函 数 名  : httpbevcb
 功能描述  : http accept回调，建立https server的关键函数，指定一个基于OpenSSL的
           bufferevent.注意,分配的bufferevent不能指定socket,即fd必须为-1。
 输入参数  : struct event_base* base
             void* arg
 输出参数  : 无
 返 回 值  : static struct bufferevent*
 调用函数  :
 被调函数  :

 修改历史      :
  1.日    期   : 2017年8月29日
    作    者   : thomas
    修改内容   : 新生成函数

*****************************************************************************/
static struct bufferevent *httpbevcb(struct event_base *base, void *arg)
{
    struct bufferevent *r;
    SSL_CTX *ctx = (SSL_CTX *) arg;
    SSL *ssl = NULL;
    ssl = SSL_new(ctx);

    if (ssl == NULL)
    {
        return NULL;
    }

    r = bufferevent_openssl_socket_new(base, -1, ssl, BUFFEREVENT_SSL_ACCEPTING,
                                       BEV_OPT_CLOSE_ON_FREE | BEV_OPT_THREADSAFE | BEV_OPT_DEFER_CALLBACKS);
    return r;
}

/*****************************************************************************
 函 数 名  : release_acception
 功能描述  : 释放保存连接客户端的bufferevent结构
 输入参数  : void * ctx             
             struct list_head * hd
 输出参数  : 无
 返 回 值  : 
 调用函数  : 
 被调函数  : 
 
 修改历史      :
  1.日    期   : 2017年9月19日
    作    者   : thomas
    修改内容   : 新生成函数

*****************************************************************************/
static int release_acception(void *ctx, struct list_head *hd)
{
    accept_bufferevent_list_ts *t;
    struct bufferevent *bev = (struct bufferevent *) ctx;


    t = container_of(hd, accept_bufferevent_list_ts, list);
    if (bev == t->bev)
    {
        DEBUG_PRINTF(INFO, "release %d", t->fd);
        list_del(&t->list);
        //bufferevent_free(t->bev);
        free(t);
        return 1;
    }
    return 0;
}

#if 0
//接口错误，废弃
/*****************************************************************************
 函 数 名  : release_all_acception
 功能描述  : 释放所有保存连接客户端的bufferevent结构
 输入参数  : void * ctx             
             struct list_head * hd
 输出参数  : 无
 返 回 值  : 
 调用函数  : 
 被调函数  : 
 
 修改历史      :
  1.日    期   : 2017年9月19日
    作    者   : thomas
    修改内容   : 新生成函数

*****************************************************************************/
static int release_all_acception(void * ctx, struct list_head * hd)
{
    accept_bufferevent_list_ts *t;

    t = container_of(hd, accept_bufferevent_list_ts, list);

    DEBUG_PRINTF(INFO, "release %d",t->fd);
    list_del(&t->list);
    bufferevent_free(t->bev);
    free(t);
    return 0;
}
#endif

/*****************************************************************************
 函 数 名  : find_acception
 功能描述  : 查找accept链表里面是否有bufferevent ctx,是返回1，否则返回0
 输入参数  : void * ctx             
             struct list_head * hd
 输出参数  : 无
 返 回 值  : static int
 调用函数  : 
 被调函数  : 
 
 修改历史      :
  1.日    期   : 2017年9月22日
    作    者   : thomas
    修改内容   : 新生成函数

*****************************************************************************/
static int find_acception(void *ctx, struct list_head *hd)
{
    accept_bufferevent_list_ts *t;
    struct bufferevent *bev = (struct bufferevent *) ctx;


    t = container_of(hd, accept_bufferevent_list_ts, list);
    if (bev == t->bev)
    {
        return 1;
    }
    return 0;
}

/*****************************************************************************
 函 数 名  : event_callback
 功能描述  : bufferevent事件回调函数，处理出错，超时，连接事件
 输入参数  : struct bufferevent* bev
             short events
             void* ctx
 输出参数  : 无
 返 回 值  : static void
 调用函数  :
 被调函数  :

 修改历史      :
  1.日    期   : 2017年8月29日
    作    者   : thomas
    修改内容   : 新生成函数

*****************************************************************************/
static void event_callback(struct bufferevent *bev, short events, void *ctx)
{
    int finished = 0;
    evsocket_config_ts *conf;
    struct list_head *pos, *tmp;
    struct accept_bufferevent_list *abl;

    if (bev == NULL || ctx == NULL)
    {
        return;
    }
    conf = (evsocket_config_ts *) ctx;

    if (events & BEV_EVENT_CONNECTED)
    {
        DEBUG_PRINTF(INFO, "connect operation finished.");
        bufferevent_enable(bev, EV_READ | EV_WRITE);
    }

    //对于TCP，EOF意味着对端关闭
    if (events & BEV_EVENT_EOF)
    {
        if (conf->role == ROLE_SERVER)
        {
            //遍历连接链表，删除节点并释放空间
            pthread_mutex_lock(&conf->acceptlist_mtx);

            list_for_each_safe(pos, tmp, &conf->acceptlist)
            {
                abl = list_last_entry(&conf->acceptlist,
                                      struct accept_bufferevent_list, list);
                release_acception(bev, &abl->list);
            }

            pthread_mutex_unlock(&conf->acceptlist_mtx);
            DEBUG_PRINTF(INFO, "close connection, free bev[%p]", bev);
            bufferevent_free(bev);
        }
        else if (conf->role == ROLE_CLIENT)
        {
            finished = 1;
        }
    }

    //如果设置了事件回调函数则调用
    if (conf->evcb)
    {
        conf->evcb(bev, events, ctx);
        return;
    }

    //否则按照默认处理方法处理事件
    if (events & BEV_EVENT_READING)
    {
        DEBUG_PRINTF(ERROR, "reading error");
    }

    if (events & BEV_EVENT_WRITING)
    {
        DEBUG_PRINTF(ERROR, "writing error");
    }

    if (events & BEV_EVENT_ERROR)
    {
        DEBUG_PRINTF(ERROR, "Got an error from %s", evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR()));
        finished = 1;
    }

    if (events & BEV_EVENT_TIMEOUT)
    {
        DEBUG_PRINTF(ERROR, "time out");
    }


    if (finished)
    {
        if (conf->role == ROLE_CLIENT)
        {
            event_base_loopexit(conf->base, NULL);
        }
    }
}

/*****************************************************************************
 函 数 名  : accept_error_cb
 功能描述  : server监听套接字出错回调函数
 输入参数  : struct evconnlistener* listener
             void* ctx
 输出参数  : 无
 返 回 值  : static void
 调用函数  :
 被调函数  :

 修改历史      :
  1.日    期   : 2017年8月29日
    作    者   : thomas
    修改内容   : 新生成函数

*****************************************************************************/
static void accept_error_cb(struct evconnlistener *listener, void *ctx)
{
    struct event_base *base = evconnlistener_get_base(listener);
    int err = EVUTIL_SOCKET_ERROR();
    DEBUG_PRINTF(ERROR, "Got an error %d (%s) on the listener.Shutting down.", err, evutil_socket_error_to_string(err));
    event_base_loopexit(base, NULL);
}

/*****************************************************************************
 函 数 名  : read_callback
 功能描述  : 读回调，如果有可读数据，则先将数据存储空间线性化，然后调用读回调。
            如果指定了定界符，则先查找可读数据中是否有定界符，没有则继续等待
            数据的接收，有则线性化存储空间，并调用回调。定界符主要用于确定一
            帧数据的界限，可以有效解决流式套接字的粘包问题。
 输入参数  : struct bufferevent* bev
             void* ctx
 输出参数  : 无
 返 回 值  : static void
 调用函数  :
 被调函数  :

 修改历史      :
  1.日    期   : 2017年8月29日
    作    者   : thomas
    修改内容   : 新生成函数
    
  2.日    期   : 2017年12月1日
    作    者   : thomas
    修改内容   : 原来的方法是搜索到定界符后，将本帧数据传给读回调处理，然后drain
              完本帧数据，但是如果input的evbuffer里面包含了2帧以上的数据，就会
              导致一次未取出所有数据，必须等到下一次读事件被触发，未取走的数据
              才会被读走。如果没数据输入，余下帧的数据将永远不会被取出。

              现在的做法是取完一帧数据后，继续尝试取下一帧，没有取到则返回

*****************************************************************************/
static void read_callback(struct bufferevent *bev, void *ctx)
{
    size_t len;
    struct evbuffer_ptr evbptr;
    unsigned char *conbuf;
    struct evbuffer *input;
    evsocket_config_ts *config;

    if (bev == NULL || ctx == NULL)
    {
        DEBUG_PRINTF(ERROR, "parameters error");
        return;
    }

    config = (evsocket_config_ts *) ctx;
    input = bufferevent_get_input(bev);

    if (input == NULL)
    {
        DEBUG_PRINTF(ERROR, "bufferevent_get_input error");
        return;
    }

    do
    {
        len = evbuffer_get_length(input);
        if (len > 0)
        {
            //如果使用定界符之类的方法，则先搜索定界符是否接收到
            if (config->delimiter != NULL)
            {
                evbptr = evbuffer_search(input, config->delimiter, config->delimiter_len, NULL);
                if (evbptr.pos == -1) //未找到字符串
                {
//                    DEBUG_PRINTF(ERROR, "not found delimiter");
                    return;
                }
            }

            //将evbuffer内的所有数据线性化
            conbuf = evbuffer_pullup(input, evbptr.pos);
            if (conbuf != NULL)
            {
                config->in(bev, (char *) conbuf, (unsigned int) (evbptr.pos));
            }
            else
            {
                DEBUG_PRINTF(ERROR, "evbuffer_pullup error");
                continue;
            }

            if (0 != evbuffer_drain(input, evbptr.pos + config->delimiter_len))
            {
                DEBUG_PRINTF(ERROR, "evbuffer_drain error");
            }
        }
    } while (len > 0);
}

/*****************************************************************************
 函 数 名  : write_callback
 功能描述  : 写回调
 输入参数  : struct bufferevent* bev
             void* ctx
 输出参数  : 无
 返 回 值  : static void
 调用函数  :
 被调函数  :

 修改历史      :
  1.日    期   : 2017年8月29日
    作    者   : thomas
    修改内容   : 新生成函数

*****************************************************************************/
static void write_callback(struct bufferevent *bev, void *ctx)
{
    int length;
    struct evbuffer *out;
    out = bufferevent_get_output(bev);
    length = evbuffer_get_length(out);

    if (length == 0)
    {
        bufferevent_disable(bev, EV_WRITE);
    }
}

/* Set "hints" in an addrinfo struct, to be passed to getaddrinfo. */
static void socket_set_hints(evsocket_config_ts *cfg, struct addrinfo *hints)
{
    if (cfg == NULL || hints == NULL)
    {
        return;
    }

    memset(hints, 0, sizeof(*hints));

    if (cfg->path)
    {
        hints->ai_family = AF_UNIX;
    }
    else
    {
        hints->ai_family = AF_UNSPEC;
    }

    hints->ai_socktype = SOCK_STREAM;
}

/*****************************************************************************
 函 数 名  : set_socket_options
 功能描述  : 设置socket选项
 输入参数  : evsocket_config_ts* cfg
             evsocket_result_ts* res
             int fd
 输出参数  : 无
 返 回 值  : static int
 调用函数  :
 被调函数  :

 修改历史      :
  1.日    期   : 2017年8月28日
    作    者   : thomas
    修改内容   : 新生成函数

*****************************************************************************/
static int set_socket_options(evsocket_config_ts *cfg, evsocket_result_ts *res, int fd)
{
    int i;

    for (i = 0; i < EVSOCKET_MAX_SOCK_OPTS; i++)
    {
        evsocket_opt_ts *opt = &cfg->sockopts[i];

        if (opt->option_id == 0)
        {
            break;
        }

        if (setsockopt(fd, SOL_SOCKET, opt->option_id, opt->value, opt->value_len) == -1)
        {
            return fail_with_errno(res, EVSOCKET_ERROR_SETSOCKOPT);
        }
    }

    return EVSOCKET_OK;
}

/*****************************************************************************
 函 数 名  : make_unixdomain
 功能描述  : 创建基于unix域套接字的server or client
 输入参数  : evsocket_config_ts* cfg,配置参数
           evsocket_result_ts* res,返回参数
 输出参数  : 无
 返 回 值  : static int
 调用函数  :
 被调函数  :

 修改历史      :
  1.日    期   : 2017年8月28日
    作    者   : thomas
    修改内容   : 新生成函数

*****************************************************************************/
static int make_unixdomain(evsocket_config_ts *cfg, evsocket_result_ts *res)
{
    size_t name_max;
    int snprintf_res;

    memset(&cfg->addr.sun, 0, sizeof(cfg->addr.sun));
    cfg->addr.sun.sun_family = AF_UNIX;
    name_max = sizeof(cfg->addr.sun.sun_path);
    snprintf_res = snprintf(cfg->addr.sun.sun_path, name_max, "%s", cfg->path);

    //路径过长
    if ((int) name_max < snprintf_res)
    {
        return fail_with_errno(res, EVSOCKET_ERROR_SNPRINTF);
    }

    if (cfg->role == ROLE_SERVER)
    {
        cfg->listener = evconnlistener_new_bind(cfg->base, accept_conn_cb, cfg, cfg->levflags, cfg->backlog,
                                                (struct sockaddr *) &(cfg->addr.sun), sizeof(cfg->addr.sun));

        if (!cfg->listener)
        {
            return fail_with_errno(res, EVSOCKET_ERROR_LISTEN);
        }

        res->fd = evconnlistener_get_fd(cfg->listener);
        evconnlistener_set_error_cb(cfg->listener, accept_error_cb);
    }
    else
    {
        if (cfg->type == EVSOCKET_TYPE_TLS)
        {
            cfg->bev = bufferevent_openssl_socket_new(cfg->base, -1, cfg->ssl,
                                                      BUFFEREVENT_SSL_CONNECTING,
                                                      cfg->bevflags);
        }
        else
        {
            cfg->bev = bufferevent_socket_new(cfg->base, -1, cfg->bevflags);
        }

        if (!cfg->bev)
        {
            return fail_with_errno(res, EVSOCKET_ERROR_BUFFEREV_SOCKET_NEW);
        }


        if (cfg->type == EVSOCKET_TYPE_TLS)
        {
            //连接关闭视为BEV_EVENT_EOF
            bufferevent_openssl_set_allow_dirty_shutdown(cfg->bev, 1);
        }

        //client端发起连接
        bufferevent_setcb(cfg->bev, read_callback, /*write_callback*/NULL, event_callback, cfg);
        if (bufferevent_socket_connect(cfg->bev, (struct sockaddr *) &(cfg->addr.sun), sizeof(cfg->addr.sun)) == -1)
        {
            /* Error starting connection */
            //bufferevent_free(cfg->bev);
            DEBUG_PRINTF(ERROR, "Got an error(%s) on the make_unixdomain.Shutting down.",
                         evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR()));
            return fail_with_errno(res, EVSOCKET_ERROR_CONNECT);
        }

        res->fd = bufferevent_getfd(cfg->bev);
    }

    if (set_socket_options(cfg, res, res->fd) != EVSOCKET_OK)
    {
        return fail_with_errno(res, EVSOCKET_ERROR_SETSOCKOPT);
    }

    return EVSOCKET_OK;
}

/*****************************************************************************
 函 数 名  : make_inet
 功能描述  : 创建基于IPv4流式套接字的server or client
 输入参数  : evsocket_config_ts* cfg,配置参数
           evsocket_result_ts* res,返回参数
 输出参数  : 无
 返 回 值  : static int
 调用函数  :
 被调函数  :

 修改历史      :
  1.日    期   : 2017年8月28日
    作    者   : thomas
    修改内容   : 新生成函数

*****************************************************************************/
static int make_inet(evsocket_config_ts *cfg, evsocket_result_ts *out)
{
    struct addrinfo hints;
    struct addrinfo *res = NULL;
    struct addrinfo *ai = NULL;
    char port_str[PORT_STR_BUFSZ];
    int addr_res;
    memset(port_str, 0, PORT_STR_BUFSZ);
    socket_set_hints(cfg, &hints);

    if (PORT_STR_BUFSZ < snprintf(port_str, PORT_STR_BUFSZ, "%u", cfg->port))
    {
        return fail_with_errno(out, EVSOCKET_ERROR_SNPRINTF);
    }

    /*getaddrinfo成功返回0，失败返回错误码，而不是-1*/
    addr_res = getaddrinfo(cfg->host, port_str, &hints, &res);

    if (addr_res != 0)
    {
        out->getaddrinfo_error = addr_res;
        freeaddrinfo(res);
        return fail_with_errno(out, EVSOCKET_ERROR_GETADDRINFO);
    }

    for (ai = res; ai != NULL; ai = ai->ai_next)
    {
        if (cfg->role == ROLE_SERVER)
        {
            cfg->listener = evconnlistener_new_bind(cfg->base, accept_conn_cb, cfg, cfg->levflags, cfg->backlog,
                                                    (struct sockaddr *) &(cfg->addr.sin), sizeof(cfg->addr.sin));

            if (!cfg->listener)
            {
                freeaddrinfo(res);
                return fail_with_errno(out, EVSOCKET_ERROR_LISTEN);
            }

            out->fd = evconnlistener_get_fd(cfg->listener);
            evconnlistener_set_error_cb(cfg->listener, accept_error_cb);
        }
        else if (cfg->role == ROLE_CLIENT)
        {
            if (cfg->type == EVSOCKET_TYPE_TLS)
            {
                cfg->bev = bufferevent_openssl_socket_new(cfg->base, -1, cfg->ssl,
                                                          BUFFEREVENT_SSL_CONNECTING,
                                                          cfg->bevflags);
            }
            else
            {
                cfg->bev = bufferevent_socket_new(cfg->base, -1, cfg->bevflags);
            }

            if (!cfg->bev)
            {
                fprintf(stderr, "Got an error(%s) on the make_inet.Shutting down.\n",
                        evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR()));
                freeaddrinfo(res);
                return fail_with_errno(out, EVSOCKET_ERROR_BUFFEREV_SOCKET_NEW);
            }

            if (cfg->type == EVSOCKET_TYPE_TLS)
            {
                bufferevent_openssl_set_allow_dirty_shutdown(cfg->bev, 1);
            }

            bufferevent_setcb(cfg->bev, read_callback, /*write_callback*/NULL, event_callback, cfg);

            if (bufferevent_socket_connect(cfg->bev, (struct sockaddr *) &(cfg->addr.sin), sizeof(cfg->addr.sin)) == -1)
            {
                /* Error starting connection */
                freeaddrinfo(res);
                return fail_with_errno(out, EVSOCKET_ERROR_CONNECT);
            }
            out->fd = bufferevent_getfd(cfg->bev);
        }
    }

    if (set_socket_options(cfg, out, out->fd) != EVSOCKET_OK)
    {
        freeaddrinfo(res);
        return fail_with_errno(out, EVSOCKET_ERROR_SETSOCKOPT);
    }

    out->status = EVSOCKET_OK;
    freeaddrinfo(res);
    out->saved_errno = 0;
    return EVSOCKET_OK;
}

/*****************************************************************************
 函 数 名  : make_http
 功能描述  : 创建http server or client
 输入参数  : evsocket_config_ts* cfg
           evsocket_result_ts* out
 输出参数  : 无
 返 回 值  : static int
 调用函数  :
 被调函数  :

 修改历史      :
  1.日    期   : 2017年8月28日
    作    者   : thomas
    修改内容   : 新生成函数

*****************************************************************************/
static int make_http(evsocket_config_ts *cfg, evsocket_result_ts *out)
{
    char *paddr;

    if (cfg == NULL || out == NULL)
    {
        return fail_with_errno(out, EVSOCKET_ERROR_INVALID);
    }

    if (cfg->role == ROLE_SERVER)
    {
        cfg->http = evhttp_new(cfg->base);
        if (!cfg->http)
        {
            DEBUG_PRINTF(ERROR, "couldn't create evhttp. Exiting.");
            return fail_with_errno(out, EVSOCKET_ERROR_HTTP_NEW);
        }

        if (cfg->type == EVSOCKET_TYPE_HTTPS)
        {
            evhttp_set_bevcb(cfg->http, httpbevcb, cfg->ssl_ctx);
        }

        if (cfg->httpgencb)
        {
            evhttp_set_gencb(cfg->http, cfg->httpgencb, cfg);
        }

#if 0
        if (cfg->httpcb)
        {
            evhttp_set_cb(cfg->http, "/dump", cfg->httpcb, cfg);
        }
#endif

        paddr = inet_ntoa(cfg->addr.sin.sin_addr);
        if (paddr == NULL)
        {
            return fail_with_errno(out, EVSOCKET_ERROR_SOCKET);
        }
        cfg->handle = evhttp_bind_socket_with_handle(cfg->http, paddr, cfg->port);
        if (!cfg->handle)
        {
            DEBUG_PRINTF(ERROR, "couldn't bind to port %d. Exiting.", cfg->port);
            return fail_with_errno(out, EVSOCKET_ERROR_BIND);
        }
    }
    else
    {
        if (cfg->type == EVSOCKET_TYPE_HTTP)
        {
            cfg->bev = bufferevent_socket_new(cfg->base, -1, cfg->bevflags);
        }
        else
        {
            cfg->bev = bufferevent_openssl_socket_new(cfg->base, -1, cfg->ssl,
                                                      BUFFEREVENT_SSL_CONNECTING,
                                                      cfg->bevflags);
        }

        if (cfg->bev == NULL)
        {
            DEBUG_PRINTF(ERROR, "bufferevent_openssl_socket_new() failed");
            return fail_with_errno(out, EVSOCKET_ERROR);
        }

        if (cfg->type == EVSOCKET_TYPE_HTTPS)
        {
            bufferevent_openssl_set_allow_dirty_shutdown(cfg->bev, 1);
        }

        cfg->evcon = evhttp_connection_base_bufferevent_new(cfg->base, NULL, cfg->bev, cfg->host, cfg->port);
        if (cfg->evcon == NULL)
        {
            DEBUG_PRINTF(ERROR, "evhttp_connection_base_bufferevent_new() failed");
            return fail_with_errno(out, EVSOCKET_ERROR);
        }
    }

    return EVSOCKET_OK;
}

/*****************************************************************************
 函 数 名  : ssl_init
 功能描述  : openssl初始化
 输入参数  : void
 输出参数  : 无
 返 回 值  : static void
 调用函数  :
 被调函数  :

 修改历史      :
  1.日    期   : 2017年8月28日
    作    者   : thomas
    修改内容   : 新生成函数

*****************************************************************************/
static void ssl_init(void)
{
#if OPENSSL_VERSION_NUMBER < 0x10100000L
    SSL_library_init();
    ERR_load_crypto_strings();
    SSL_load_error_strings();
    OpenSSL_add_all_algorithms();
#endif
}

/*static int cert_verify_callback(X509_STORE_CTX *x509_ctx, void *arg)
{
    char cert_str[256];
    const char *host = (const char *) arg;
    const char *res_str = "X509_verify_cert failed";
//    HostnameValidationResult res = Error;

*//*     This is the function that OpenSSL would call if we hadn't called
     * SSL_CTX_set_cert_verify_callback().  Therefore, we are "wrapping"
     * the default functionality, rather than replacing it. *//*
    int ok_so_far = 0;

    X509 *server_cert = NULL;

//    if (ignore_cert)
//    {
//        return 1;
//    }

    ok_so_far = X509_verify_cert(x509_ctx);

    server_cert = X509_STORE_CTX_get_current_cert(x509_ctx);

//    if (ok_so_far)
//    {
//        res = validate_hostname(host, server_cert);
//
//        switch (res)
//        {
//            case MatchFound:
//                res_str = "MatchFound";
//                break;
//            case MatchNotFound:
//                res_str = "MatchNotFound";
//                break;
//            case NoSANPresent:
//                res_str = "NoSANPresent";
//                break;
//            case MalformedCertificate:
//                res_str = "MalformedCertificate";
//                break;
//            case Error:
//                res_str = "Error";
//                break;
//            default:
//                res_str = "WTF!";
//                break;
//        }
//    }

    X509_NAME_oneline(X509_get_subject_name(server_cert),
                      cert_str, sizeof(cert_str));

//    if (res == MatchFound)
//    {
//        printf("https server '%s' has this certificate, "
//               "which looks good to me:\n%s\n",
//               host, cert_str);
//        return 1;
//    }
//    else
//    {
//        printf("Got '%s' for hostname '%s' and certificate:\n%s\n",
//               res_str, host, cert_str);
//        return 0;
//    }
    return 1;
}*/

/*****************************************************************************
 函 数 名  : ssl_setup_certs
 功能描述  : 设置https server的证书，私钥等信息
 输入参数  : SSL_CTX *ctx
             const char *server_crt
             const char *private_key
 输出参数  : 无
 返 回 值  : static int
 调用函数  :
 被调函数  :

 修改历史      :
  1.日    期   : 2017年8月28日
    作    者   : thomas
    修改内容   : 新生成函数

*****************************************************************************/
static int ssl_setup_certs(SSL_CTX *ctx, const char *server_crt, const char *private_key)
{
    //设置由CA机构签发的server证书
    if (1 != SSL_CTX_use_certificate_chain_file(ctx, server_crt))
    {
        return -1;
    }

    //与server证书对应的server私钥
    if (1 != SSL_CTX_use_PrivateKey_file(ctx, private_key, SSL_FILETYPE_PEM))
    {
        return -1;
    }

    //检查server证书和server私钥是否匹配
    if (1 != SSL_CTX_check_private_key(ctx))
    {
        return -1;
    }

    return EVSOCKET_OK;
}

/*****************************************************************************
 函 数 名  : evsocket_create
 功能描述  : 创建一个流式套接字类型的网络通信端的server
                 or client
 输入参数  : evsocket_config_ts* cfg
           evsocket_result_ts* res
 输出参数  : 无
 返 回 值  :
 调用函数  :
 被调函数  :

 修改历史      :
  1.日    期   : 2017年8月28日
    作    者   : thomas
    修改内容   : 新生成函数

*****************************************************************************/
int evsocket_create(evsocket_config_ts *cfg, evsocket_result_ts *res, struct event_base *attachbase)
{
    if (cfg == NULL || res == NULL || attachbase == NULL)
    {
        return EVSOCKET_ERROR;
    }

    memset(res, 0, sizeof(*res));

    //共用一个base
    if (attachbase)
    {
        cfg->base = attachbase;
    }

    if (check_config(cfg, res) != EVSOCKET_OK)
    {
        res->status = EVSOCKET_ERROR_CONFIGURATION;
        return EVSOCKET_ERROR;
    }

    if (cfg->type == EVSOCKET_TYPE_TCP || cfg->type == EVSOCKET_TYPE_TLS)
    {
        if (cfg->af == EV_FAMILY_UNIXDOMAIN)
        {
            if (make_unixdomain(cfg, res) != EVSOCKET_OK)
            {
                return EVSOCKET_ERROR;
            }
        }
        else if (cfg->af == EV_FAMILY_INET)
        {
            if (make_inet(cfg, res) != EVSOCKET_OK)
            {
                return EVSOCKET_ERROR;
            }
        }
    }

    if (cfg->type == EVSOCKET_TYPE_HTTP || cfg->type == EVSOCKET_TYPE_HTTPS)
    {
        if (make_http(cfg, res) != EVSOCKET_OK)
        {
            return EVSOCKET_ERROR;
        }
    }

    return EVSOCKET_OK;
}

/*****************************************************************************
 函 数 名  : evsocket_release
 功能描述  : 资源释放
 输入参数  : evsocket_config_ts* cfg
           evsocket_result_ts* res
 输出参数  : 无
 返 回 值  :
 调用函数  :
 被调函数  :

 修改历史      :
  1.日    期   : 2017年8月28日
    作    者   : thomas
    修改内容   : 新生成函数
    
  2.日    期   : 2017年12月18日
    作    者   : thomas
    修改内容   : 加强对指针的判断，并且释放后置为NULL

*****************************************************************************/
int evsocket_release(evsocket_config_ts *cfg, evsocket_result_ts *res)
{
    struct list_head *pos, *tmp;
    struct accept_bufferevent_list *abl;

    if (cfg->listener)
    {
        evconnlistener_free(cfg->listener);
        cfg->listener = NULL;
    }

    if (cfg->bev)
    {
        //http & https client 在evhttp_connection_free 中释放bufferevent
        if (cfg->evcon == NULL)
        {
            bufferevent_free(cfg->bev);
            cfg->bev = NULL;
        }
    }

    if (cfg->evcon)
    {
        evhttp_connection_free(cfg->evcon);
        cfg->evcon = NULL;
    }

    if (cfg->http_uri)
    {
        evhttp_uri_free(cfg->http_uri);
        cfg->http_uri = NULL;
    }

    if (cfg->http)
    {
        evhttp_free(cfg->http);
        cfg->http = NULL;
    }

    if (cfg->role == ROLE_SERVER && (cfg->type == EVSOCKET_TYPE_TLS || cfg->type == EVSOCKET_TYPE_TCP))
    {
        pthread_mutex_lock(&cfg->acceptlist_mtx);

        list_for_each_safe(pos, tmp, &cfg->acceptlist)
        {
            abl = list_last_entry(&cfg->acceptlist,
                                  struct accept_bufferevent_list, list);
            release_acception(NULL, &abl->list);
        }

        pthread_mutex_unlock(&cfg->acceptlist_mtx);
        pthread_mutex_destroy(&cfg->acceptlist_mtx);
    }

    if (cfg->type == EVSOCKET_TYPE_TLS || cfg->type == EVSOCKET_TYPE_HTTPS)
    {
        if (cfg->ssl_ctx != NULL)
        {
            SSL_CTX_free(cfg->ssl_ctx);
            cfg->ssl_ctx = NULL;
        }

#if 0
            /*如果bufferevent在创建时指定了BEV_OPT_CLOSE_ON_FREE选项或者
            evconnlistener在创建时指定了LEV_OPT_CLOSE_ON_FREE选项，释放
            对应结构时，相应的fd,bufferevent或者相关类似的资源也被释放

            LEV_OPT_CLOSE_ON_FREE:会自动释放fd, see:event_listener_destroy
            BEV_OPT_CLOSE_ON_FREE:自动关闭fd,释放 SSL see:be_openssl_destruct

            */
            if ( cfg->ssl != NULL )
            {
                SSL_free(cfg->ssl);
            }

#endif
        ERR_free_strings();
        CRYPTO_cleanup_all_ex_data();
    }

    return EVSOCKET_OK;
}

/*****************************************************************************
 函 数 名  : evsocket_write
 功能描述  : 网络通信的写函数，http类型的不采用此接口
 输入参数  : evsocket_config_ts* cfg
             evsocket_result_ts* res
             const char *buf:写数据指针
             int length:数据长度
 输出参数  : 无
 返 回 值  :
 调用函数  :
 被调函数  :

 修改历史      :
  1.日    期   : 2017年8月28日
    作    者   : thomas
    修改内容   : 新生成函数

*****************************************************************************/
int evsocket_write(evsocket_config_ts *cfg, evsocket_result_ts *res, struct bufferevent *bevent, const char *buf,
                   int length)
{
    int exist = 0;
    struct evbuffer *output;
    struct list_head *pos, *tmp;
    struct accept_bufferevent_list *abl;

    if (cfg == NULL || res == NULL || buf == NULL || bevent == NULL)
    {
        DEBUG_PRINTF(ERROR, "parameters error");
        return fail_with_errno(res, EVSOCKET_ERROR_INVALID);
    }

    if (cfg->bev == NULL && bevent == NULL)
    {
        DEBUG_PRINTF(ERROR, "bufferevent error");
        return fail_with_errno(res, EVSOCKET_ERROR_INVALID);
    }

    if (cfg->role == ROLE_CLIENT)
    {
        output = bufferevent_get_output(cfg->bev);
    }
    else
    {
        pthread_mutex_lock(&cfg->acceptlist_mtx);

        list_for_each_safe(pos, tmp, &cfg->acceptlist)
        {
            abl = list_last_entry(&cfg->acceptlist,
                                  struct accept_bufferevent_list, list);
            exist = find_acception(bevent, &abl->list);
            if (exist)
            {
                break;
            }
        }

        if (exist)
        {
            output = bufferevent_get_output(bevent);
        }
        else
        {
            DEBUG_PRINTF(ERROR, "not found bev:%p in accept list", bevent);
            output = NULL;
        }
        pthread_mutex_unlock(&cfg->acceptlist_mtx);
    }

    if (output == NULL)
    {
        DEBUG_PRINTF(ERROR, "output is NULL");
        return fail_with_errno(res, EVSOCKET_ERROR_INVALID);
    }

    if (-1 == evbuffer_add(output, buf, length))
    {
        DEBUG_PRINTF(ERROR, "evbuffer_add error");
        return fail_with_errno(res, EVSOCKET_ERROR_INVALID);
    }

    if (-1 == evbuffer_add(output, cfg->delimiter, cfg->delimiter_len))
    {
        DEBUG_PRINTF(ERROR, "evbuffer_add error");
        return fail_with_errno(res, EVSOCKET_ERROR_INVALID);
    }

    if (exist)
    {
        bufferevent_enable(bevent, EV_WRITE);
    }

    return EVSOCKET_OK;
}

/*****************************************************************************
 函 数 名  : evsocket_http_uri_parse
 功能描述  : 解析uri
 输入参数  : evsocket_config_ts* cfg
             evsocket_result_ts* out
             const char* url
 输出参数  : 无
 返 回 值  :
 调用函数  :
 被调函数  :

 修改历史      :
  1.日    期   : 2017年8月28日
    作    者   : thomas
    修改内容   : 新生成函数

*****************************************************************************/
int evsocket_http_uri_parse(evsocket_config_ts *cfg, evsocket_result_ts *out, const char *url)
{
    const char *scheme;
    cfg->http_uri = evhttp_uri_parse(url);

    if (cfg->http_uri == NULL)
    {
        DEBUG_PRINTF(ERROR, "malformed url");
        return fail_with_errno(out, EVSOCKET_ERROR_INVALID);
    }

    scheme = evhttp_uri_get_scheme(cfg->http_uri);

    if (scheme == NULL || (strcasecmp(scheme, "https") != 0 &&
                           strcasecmp(scheme, "http") != 0))
    {
        DEBUG_PRINTF(ERROR, "url must be http or https");
        return fail_with_errno(out, EVSOCKET_ERROR_INVALID);
    }

    cfg->host = evhttp_uri_get_host(cfg->http_uri);

    if (cfg->host == NULL)
    {
        DEBUG_PRINTF(ERROR, "url must have a host");
        return fail_with_errno(out, EVSOCKET_ERROR_INVALID);
    }

#if 0
    cfg->port = evhttp_uri_get_port(cfg->http_uri);

    if (cfg->port == 0xFFFF)
    {
        cfg->port = (strcasecmp(scheme, "http") == 0) ? 80 : 443;
    }
#endif

#if 0
    cfg->http_path = evhttp_uri_get_path(cfg->http_uri);

    if (strlen(cfg->http_path) == 0)
    {
        cfg->http_path = "/";
    }

    cfg->http_query = evhttp_uri_get_query(cfg->http_uri);
#endif
    return EVSOCKET_OK;
}

/*****************************************************************************
 函 数 名  : evsocket_http_add_header
 功能描述  : 向http请求添加header字段
 输入参数  : evsocket_config_ts* cfg
             evsocket_result_ts* res
             const char *hfname
             const char *hfvalue
 输出参数  : 无
 返 回 值  :
 调用函数  :
 被调函数  :

 修改历史      :
  1.日    期   : 2017年8月28日
    作    者   : thomas
    修改内容   : 新生成函数

*****************************************************************************/
int evsocket_http_add_header(evsocket_config_ts *cfg, evsocket_result_ts *res, const char *hfname, const char *hfvalue)
{
    if (NULL == cfg || NULL == res || NULL == hfname || NULL == hfvalue)
    {
        return fail_with_errno(res, EVSOCKET_ERROR_INVALID);
    }

    if (cfg->output_headers == NULL)
    {
        return fail_with_errno(res, EVSOCKET_ERROR_INVALID);
    }

    return evhttp_add_header(cfg->output_headers, hfname, hfvalue);
}

/*****************************************************************************
 函 数 名  : evsocket_http_connection_setup
 功能描述  : 设置http参数，包括重试次数和超时
 输入参数  : evsocket_config_ts* cfg
             evsocket_result_ts* res
 输出参数  : 无
 返 回 值  :
 调用函数  :
 被调函数  :

 修改历史      :
  1.日    期   : 2017年8月28日
    作    者   : thomas
    修改内容   : 新生成函数

*****************************************************************************/
int evsocket_http_connection_setup(evsocket_config_ts *cfg, evsocket_result_ts *res)
{
    if (NULL == cfg || NULL == res)
    {
        return fail_with_errno(res, EVSOCKET_ERROR_INVALID);
    }

    if (cfg->retries > 0)
    {
        evhttp_connection_set_retries(cfg->evcon, cfg->retries);
    }

    if (cfg->timeout > 0)
    {
        evhttp_connection_set_timeout(cfg->evcon, cfg->timeout);
    }

    return EVSOCKET_OK;
}

/*****************************************************************************
 函 数 名  : evsocket_http_request_new
 功能描述  : 创建一个http请求实体
 输入参数  : evsocket_config_ts* cfg
             evsocket_result_ts* res
 输出参数  : 无
 返 回 值  :
 调用函数  :
 被调函数  :

 修改历史      :
  1.日    期   : 2017年8月28日
    作    者   : thomas
    修改内容   : 新生成函数

*****************************************************************************/
int evsocket_http_request_new(evsocket_config_ts *cfg, evsocket_result_ts *res,
                              void (*httpreqdonecb)(struct evhttp_request *, void *))
{
    //libevent会自动free掉 new 的struct evhttp_request
    cfg->req = evhttp_request_new(httpreqdonecb, cfg->bev);

    if (cfg->req == NULL)
    {
        DEBUG_PRINTF(ERROR, "evhttp_request_new() failed");
        return fail_with_errno(res, EVSOCKET_ERROR_INVALID);
    }

    cfg->output_headers = evhttp_request_get_output_headers(cfg->req);

    if (cfg->output_headers == NULL)
    {
        return fail_with_errno(res, EVSOCKET_ERROR_INVALID);
    }

    return EVSOCKET_OK;
}

/*****************************************************************************
 函 数 名  : evsocket_http_make_request
 功能描述  : 发起http请求
 输入参数  : evsocket_config_ts* cfg
             evsocket_result_ts* res
             enum evhttp_cmd_type type:请求类型
 输出参数  : 无
 返 回 值  :
 调用函数  :
 被调函数  :

 修改历史      :
  1.日    期   : 2017年8月28日
    作    者   : thomas
    修改内容   : 新生成函数

*****************************************************************************/
int evsocket_http_make_request(evsocket_config_ts *cfg, evsocket_result_ts *res, enum evhttp_cmd_type type, char *uri)
{
    return evhttp_make_request(cfg->evcon, cfg->req, type, uri);
}

/*****************************************************************************
 函 数 名  : evsocket_http_setcb
 功能描述  : http server使用的函数，设置指定URI的回调函数

 输入参数  : evsocket_config_ts* cfg
             evsocket_result_ts* res
             const char *path
             void (*cb)(struct evhttp_request *
             void *)
             void *cbargs
 输出参数  : 无
 返 回 值  :
 调用函数  :
 被调函数  :

 修改历史      :
  1.日    期   : 2017年8月28日
    作    者   : thomas
    修改内容   : 新生成函数

*****************************************************************************/
int evsocket_http_setcb(evsocket_config_ts *cfg, evsocket_result_ts *res, const char *path,
                        void (*cb)(struct evhttp_request *, void *), void *cbargs)
{
    /* The /dump URI will dump all requests to stdout and say 200 ok. */
    return evhttp_set_cb(cfg->http, path, cb, cbargs);
}

/*****************************************************************************
 函 数 名  : evsocket_http_setgencb
 功能描述  : http server使用函数，设置通用的URI回调函数
 输入参数  : evsocket_config_ts* cfg
             evsocket_result_ts* res
             void (*gencb)(struct evhttp_request *
             void *)
             void *cbargs
 输出参数  : 无
 返 回 值  :
 调用函数  :
 被调函数  :

 修改历史      :
  1.日    期   : 2017年8月28日
    作    者   : thomas
    修改内容   : 新生成函数

*****************************************************************************/
int evsocket_http_setgencb(evsocket_config_ts *cfg, evsocket_result_ts *res,
                           void (*gencb)(struct evhttp_request *, void *), void *cbargs)
{
    /* We want to accept arbitrary requests, so we need to set a "generic"
     * cb.  We can also add callbacks for specific paths. */
    evhttp_set_gencb(cfg->http, gencb, cbargs);
    return EVSOCKET_OK;
}

int evsocket_init_as_inet_server(evsocket_config_ts *config,
                                 bool withtls,
                                 char *const ca,
                                 char *const sca,
                                 char *const pk,
                                 char *const ip,
                                 unsigned short port,
                                 evsocket_callback r,
                                 evsocket_callback w,
                                 evsocket_event_callback evc,
                                 char *const delimter)
{
    if (config == NULL || ip == NULL)
    {
        return EVSOCKET_ERROR_INVALID;
    }

    memset(config, 0, sizeof(evsocket_config_ts));
    config->levflags = LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE | LEV_OPT_THREADSAFE;
    config->bevflags = BEV_OPT_CLOSE_ON_FREE | BEV_OPT_THREADSAFE /*| BEV_OPT_DEFER_CALLBACKS*/;
    config->role = ROLE_SERVER;

    if (withtls)
    {
        config->type = EVSOCKET_TYPE_TLS;
        config->ca_crt = ca;
        config->server_crt = sca;
        config->private_key = pk;
    }
    else
    {
        config->type = EVSOCKET_TYPE_TCP;

    }

    config->af = EV_FAMILY_INET;
    bzero(&config->addr.sin, sizeof(struct sockaddr_in));
    config->addr.sin.sin_family = AF_INET;
    config->addr.sin.sin_port = htons(port);
    config->addr.sin.sin_addr.s_addr = inet_addr(ip);
    config->host = ip;
    config->backlog = -1;

    config->out = w;
    config->in = r;
    config->evcb = evc;
    config->delimiter = delimter;
    config->delimiter_len = strlen(delimter);

    return EVSOCKET_OK;
}

int evsocket_init_as_inet_client(evsocket_config_ts *config,
                                 bool withtls,
                                 char *const ca,
                                 char *const ip,
                                 unsigned short port,
                                 evsocket_callback r,
                                 evsocket_callback w,
                                 evsocket_event_callback evc,
                                 char *const delimter)
{
    if (config == NULL || ip == NULL)
    {
        return EVSOCKET_ERROR_INVALID;
    }

    memset(config, 0, sizeof(evsocket_config_ts));
    config->levflags = LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE | LEV_OPT_THREADSAFE;
    config->bevflags = BEV_OPT_CLOSE_ON_FREE | BEV_OPT_THREADSAFE /*| BEV_OPT_DEFER_CALLBACKS*/;
    config->role = ROLE_CLIENT;

    if (withtls)
    {
        config->type = EVSOCKET_TYPE_TLS;
        config->ca_crt = ca;
    }
    else
    {
        config->type = EVSOCKET_TYPE_TCP;
    }

    config->af = EV_FAMILY_INET;

    bzero(&config->addr.sin, sizeof(struct sockaddr_in));
    config->addr.sin.sin_family = AF_INET;
    config->addr.sin.sin_port = htons(port);
    config->addr.sin.sin_addr.s_addr = inet_addr(ip);
    config->host = ip;
    config->backlog = -1;

    config->out = w;
    config->in = r;
    config->evcb = evc;
    config->delimiter = delimter;
    config->delimiter_len = strlen(delimter);

    return EVSOCKET_OK;
}

int evsocket_init_as_unix_domain_server(evsocket_config_ts *config,
                                        bool withtls,
                                        char *const ca,
                                        char *const sca,
                                        char *const pk,
                                        char *const path,
                                        evsocket_callback r,
                                        evsocket_callback w,
                                        evsocket_event_callback evc,
                                        char *const delimter)
{
    if (config == NULL)
    {
        return EVSOCKET_ERROR_INVALID;
    }

    memset(config, 0, sizeof(evsocket_config_ts));
    config->levflags = LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE | LEV_OPT_THREADSAFE;
    config->bevflags = BEV_OPT_CLOSE_ON_FREE | BEV_OPT_THREADSAFE /*| BEV_OPT_DEFER_CALLBACKS*/;
    config->role = ROLE_SERVER;

    if (withtls)
    {
        config->type = EVSOCKET_TYPE_TLS;
        config->ca_crt = ca;
        config->server_crt = sca;
        config->private_key = pk;
    }
    else
    {
        config->type = EVSOCKET_TYPE_TCP;
    }
    config->path = path;
    config->af = EV_FAMILY_UNIXDOMAIN;
    config->backlog = -1;

    config->out = w;
    config->in = r;
    config->evcb = evc;
    config->delimiter = delimter;
    config->delimiter_len = strlen(delimter);

    return EVSOCKET_OK;
}

int evsocket_init_as_unix_domain_client(evsocket_config_ts *config,
                                        bool withtls,
                                        char *const ca,
                                        char *const path,
                                        evsocket_callback r,
                                        evsocket_callback w,
                                        evsocket_event_callback evc,
                                        char *const delimter)
{
    if (config == NULL)
    {
        return EVSOCKET_ERROR_INVALID;
    }

    memset(config, 0, sizeof(evsocket_config_ts));
    config->levflags = LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE | LEV_OPT_THREADSAFE;
    config->bevflags = BEV_OPT_CLOSE_ON_FREE | BEV_OPT_THREADSAFE /*| BEV_OPT_DEFER_CALLBACKS*/;
    config->role = ROLE_CLIENT;

    if (withtls)
    {
        config->type = EVSOCKET_TYPE_TLS;
        config->ca_crt = ca;
    }
    else
    {
        config->type = EVSOCKET_TYPE_TCP;
    }

    config->path = path;
    config->af = EV_FAMILY_UNIXDOMAIN;
    config->backlog = -1;

    config->out = w;
    config->in = r;
    config->evcb = evc;
    config->delimiter = delimter;
    config->delimiter_len = strlen(delimter);

    return EVSOCKET_OK;
}

int evsocket_init_as_http_server(evsocket_config_ts *config,
                                 bool withtls,
                                 char *const ca,
                                 char *const sca,
                                 char *const pk,
                                 char *const ip,
                                 unsigned short port,
                                 void (*httpgencb)(struct evhttp_request *, void *))
{
    if (config == NULL || ip == NULL)
    {
        return EVSOCKET_ERROR_INVALID;
    }

    memset(config, 0, sizeof(evsocket_config_ts));
    config->levflags = LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE | LEV_OPT_THREADSAFE;
    config->bevflags = BEV_OPT_CLOSE_ON_FREE | BEV_OPT_THREADSAFE /*| BEV_OPT_DEFER_CALLBACKS*/;
    config->role = ROLE_SERVER;

    if (withtls)
    {
        config->type = EVSOCKET_TYPE_HTTPS;
        config->ca_crt = ca;
        config->server_crt = sca;
        config->private_key = pk;
    }
    else
    {
        config->type = EVSOCKET_TYPE_HTTP;

    }

    bzero(&config->addr.sin, sizeof(struct sockaddr_in));
    config->addr.sin.sin_family = AF_INET;
    config->addr.sin.sin_port = htons(port);
    config->addr.sin.sin_addr.s_addr = inet_addr(ip);
    config->host = ip;
    config->backlog = -1;
    config->port = port;
    config->httpgencb = httpgencb;

    return EVSOCKET_OK;
}

int evsocket_init_as_http_client(evsocket_config_ts *config,
                                 bool withtls,
                                 char *const ca,
                                 char *const ip,
                                 unsigned short port/*,
                                        void (*httpreqdonecb)(struct evhttp_request *, void *)*/)
{
    if (config == NULL || ip == NULL)
    {
        return EVSOCKET_ERROR_INVALID;
    }

    memset(config, 0, sizeof(evsocket_config_ts));
    config->levflags = LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE | LEV_OPT_THREADSAFE;
    config->bevflags = BEV_OPT_CLOSE_ON_FREE | BEV_OPT_THREADSAFE /*| BEV_OPT_DEFER_CALLBACKS*/;
    config->role = ROLE_CLIENT;

    if (withtls)
    {
        config->type = EVSOCKET_TYPE_HTTPS;
        config->ca_crt = ca;
    }
    else
    {
        config->type = EVSOCKET_TYPE_HTTP;
    }

    bzero(&config->addr.sin, sizeof(struct sockaddr_in));
    config->addr.sin.sin_family = AF_INET;
    config->addr.sin.sin_port = htons(port);
    config->addr.sin.sin_addr.s_addr = inet_addr(ip);
    config->host = ip;
    config->backlog = -1;
    //config->httpreqdonecb = httpreqdonecb;
    config->port = port;

    return EVSOCKET_OK;
}
